#include "Renderable.h"
#include "HelperD3D.h"
#include "DirectX11.h"
#include "VertexShader.csh"
#include "PixelShader.csh"
#include "DDSTextureLoader.h"
#include <string>
//#include <DirectXMath.h>
//using namespace DirectX;



Renderable::Renderable()
{
	bPositions = nullptr;
	bNormals = nullptr;
	bUVs = nullptr;
	bTangents = nullptr;
	bIndices = nullptr;
	bObjectCB = nullptr;
	inputLayout = nullptr;
	samplerState = nullptr;
	ZeroMemory(&strides, sizeof(UINT)* NUMBUFFERS);
	ZeroMemory(&offsets, sizeof(UINT)* NUMBUFFERS);
	numVerts = numIndices = 0;
	topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
	XMStoreFloat4x4(&object.mWorld,XMMatrixIdentity());
	key = "";
}
Renderable::Renderable(RenderInfo info){
	bPositions = info.buffers[0];
	bNormals = info.buffers[1];
	bUVs = info.buffers[2];
	bTangents = info.buffers[3];
	bIndices = info.indexBuffer;
	bObjectCB = info.objectCB;
	inputLayout = info.inputLayout;
	memcpy(offsets, info.offsets, sizeof(UINT)*NUMBUFFERS);
	memcpy(strides, info.strides, sizeof(UINT)*NUMBUFFERS);
	numVerts = info.numVerts;
	numIndices = info.numIndices;
	topology = info.topology;
	samplerState = info.samplerState;
}


Renderable::~Renderable()
{
	//Free Memory
	//Add SAFE_RELEASE
	SAFE_RELEASE(bPositions);
	SAFE_RELEASE(bNormals);
	SAFE_RELEASE(bUVs);
	SAFE_RELEASE(bTangents);
	SAFE_RELEASE(bIndices);
	SAFE_RELEASE(bObjectCB);
	SAFE_RELEASE(inputLayout);
	SAFE_RELEASE(vs);
	SAFE_RELEASE(ps);
	SAFE_RELEASE(samplerState);
}

RenderInfo Renderable::GetRenderInformation(){
	RenderInfo info;
	info.buffers[0] = bPositions;
	info.buffers[1] = bNormals;
	info.buffers[2] = bUVs;
	info.buffers[3] = bTangents;
	info.indexBuffer = bIndices;
	info.inputLayout = inputLayout;
	memcpy(info.offsets, offsets, sizeof(UINT)*NUMBUFFERS);
	memcpy(info.strides, strides, sizeof(UINT)*NUMBUFFERS);
	info.numVerts = numVerts;
	info.numIndices = numIndices;
	info.numBuffers = NUMBUFFERS;
	info.topology = topology;
	info.vs = vs;
	info.ps = ps;
	info.objectCB = bObjectCB;
	info.object = object;
	//info.srv = DirectX11::GetInstance()->GetTexture(baseTexture);
	//info.normalMapSRV = DirectX11::GetInstance()->GetTexture(normalMap);
	info.samplerState = samplerState;
	info.srv = DirectX11::GetInstance()->GetTexture(key);
	info.normalMapSRV = DirectX11::GetInstance()->GetTexture(normalKey);

	return info;
}

void Renderable::SetBuffer(ID3D11Buffer* buffer, BufferType type, UINT* strides, UINT* offsets){
	switch (type){
	case POS:
		bPositions = buffer;
		break;
	case NORM:
		bNormals = buffer;
		break;
	case UV:
		bUVs = buffer;
		break;
	case TAN:
		bTangents = buffer;
		break;
	case IND:
		bIndices = buffer;
		break;
	case CB:
		bObjectCB = buffer;
		break;
	}

	if (strides)
		memcpy(this->strides, strides, NUMBUFFERS*sizeof(UINT));
	if (offsets)
		memcpy(this->offsets, offsets, NUMBUFFERS*sizeof(UINT));
}

//To be moved to a derived class
void Renderable::InitializeObject(){

	ID3D11Device* device = DirectX11::GetInstance()->GetDevice();

	XMFLOAT3* positions;
	XMFLOAT3* normals;
	XMFLOAT2* uvs;
	XMFLOAT2* tangents;
	UINT* indices;
	CreatePlane(5, &positions, &normals, &uvs, &tangents, &indices);

	numIndices = 6;
	numVerts = 6;

	D3D11_BUFFER_DESC desc;
	desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	desc.Usage = D3D11_USAGE_IMMUTABLE;
	desc.CPUAccessFlags = NULL;
	desc.ByteWidth = sizeof(XMFLOAT3)* 4;
	desc.MiscFlags = NULL;

	D3D11_SUBRESOURCE_DATA init;
	init.pSysMem = positions;
	init.SysMemPitch = init.SysMemSlicePitch = 0;

	HRESULT hr = device->CreateBuffer(&desc, &init, &bPositions);
	ERRORCHECK(hr, L"ERROR: Failed to create positions buffer RENDERABLE");

	init.pSysMem = normals;
	hr = device->CreateBuffer(&desc, &init, &bNormals);
	ERRORCHECK(hr, L"ERROR: Failed to create normals buffer RENDERABLE");

	desc.ByteWidth = sizeof(XMFLOAT2)* 4;
	init.pSysMem = tangents;
	hr = device->CreateBuffer(&desc, &init, &bTangents);
	ERRORCHECK(hr, L"ERROR: Failed to create tangents buffer RENDERABLE");

	init.pSysMem = uvs;
	hr = device->CreateBuffer(&desc, &init, &bUVs);
	ERRORCHECK(hr, L"ERROR: Failed to create uvs buffer RENDERABLE");

	desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	desc.ByteWidth = sizeof(UINT)* 6;
	init.pSysMem = indices;
	hr = device->CreateBuffer(&desc, &init, &bIndices);
	ERRORCHECK(hr, L"ERROR: Failed to create indices buffer RENDERABLE");

	strides[0] = sizeof(XMFLOAT3);
	strides[1] = strides[0];
	strides[2] = sizeof(XMFLOAT2);
	strides[3] = strides[2];

	offsets[0] = offsets[1] = offsets[2] = offsets[3] = 0;

	hr = device->CreateVertexShader(VertexShader, sizeof(VertexShader), NULL, &vs);
	ERRORCHECK(hr, L"ERROR: Failed to Create Vertex Shader\n\tMay be due to problem in shader");

	hr = device->CreatePixelShader(PixelShader, sizeof(PixelShader), NULL, &ps);
	ERRORCHECK(hr, L"ERROR: Failed to Create Vertex Shader\n\tMay be due to problem in shader");

	//Create Input Layouts...
	D3D11_INPUT_ELEMENT_DESC layout[] = {
		{ LPCSTR("POSITION"), 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA },
		{ LPCSTR("NORMAL"), 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA },
		{ LPCSTR("TEXCOORD"), 0, DXGI_FORMAT_R32G32_FLOAT, 2, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA },
		{ LPCSTR("TANGENT"), 0, DXGI_FORMAT_R32G32_FLOAT, 3, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA }
	};

	hr = device->CreateInputLayout(layout, ARRAYSIZE(layout), VertexShader, sizeof(VertexShader), &inputLayout);
	ERRORCHECK(hr, L"ERROR: Failed to create input layout");

	//Create Buffers...
	ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
	desc.Usage = D3D11_USAGE_DYNAMIC;
	desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	desc.MiscFlags = 0;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	desc.ByteWidth = sizeof(OBJECT);
	desc.StructureByteStride = 0;

	D3D11_SUBRESOURCE_DATA init_data;
	ZeroMemory(&init_data, sizeof(D3D11_SUBRESOURCE_DATA));
	init_data.pSysMem = &object;
	init_data.SysMemPitch = 0;
	init_data.SysMemSlicePitch = 0;

	hr = device->CreateBuffer(&desc, &init_data, &bObjectCB);
	ERRORCHECK(hr, L"ERROR: Failed to create Object CB");

	D3D11_SAMPLER_DESC sampDesc;
	ZeroMemory(&desc, sizeof(D3D11_SAMPLER_DESC));
	sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
	sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
	sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
	sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	sampDesc.MinLOD = -FLT_MAX;
	sampDesc.MaxLOD = FLT_MAX;
	sampDesc.MipLODBias = 0.0f;
	sampDesc.MaxAnisotropy = 1;
	sampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;

	hr = device->CreateSamplerState(&sampDesc,&samplerState);
	ERRORCHECK(hr,L"ERROR: Failed to create Sampler State");

	//LoadTexture(L"182.dds");


	delete [] positions;
	delete [] normals;
	delete [] uvs;
	delete [] tangents;
	delete [] indices;

}

void Renderable::LoadTexture(wchar_t* fileName){
	return;
	std::wstring filePath = L"../Resources/Images/";
	filePath += fileName;
	ID3D11Device* device = DirectX11::GetInstance()->GetDevice();

	//HRESULT hr = CreateDDSTextureFromFile(device,L"../Resources/Images/testNormalMap.dds",NULL,&srv);
//	HRESULT hr = CreateDDSTextureFromFile(device,L"../Resources/Images/182.dds",NULL,&srv);
	//HRESULT hr = CreateDDSTextureFromFile(device,filePath.c_str(),NULL,&srv);
	//HRESULT hr = CreateDDSTextureFromFile(device,L"../Resources/Images/182.dds",NULL,&srv);
	//ERRORCHECK(hr,L"ERROR: Failed to create/find texture/srv");

	//hr = CreateDDSTextureFromFile(device,L"../Resources/Images/testNormalMap.dds",NULL,&normalMapSRV);
	//hr = CreateDDSTextureFromFile(device,L"../Resources/Images/182_norm.dds",NULL,&normalMapSRV);
	//hr = CreateDDSTextureFromFile(device,L"../Resources/Images/BrickNormal.dds",NULL,&normalMapSRV);
	//ERRORCHECK(hr,L"ERROR: Failed to create/find normal/srv");


}

void Renderable::SetRenderData(RenderInfo& data)
{
	this->bIndices = data.indexBuffer; 
	this->bPositions = data.buffers[POS];
	this->bNormals = data.buffers[NORM];
	this->bTangents = data.buffers[TAN];
	this->bUVs = data.buffers[UV];
	this->bObjectCB = data.objectCB;
	this->object = data.object;
	this->inputLayout = data.inputLayout;
	this->numVerts = data.numVerts;
	this->numBuffers = data.numBuffers;
	this->numIndices = data.numIndices;
	for(int i = 0; i < data.numBuffers; i++){
		this->offsets[i] = data.offsets[i];
		this->strides[i] = data.strides[i];
	}
	this->ps = data.ps;
	this->vs = data.vs;
	this->topology = data.topology;
	this->samplerState = data.samplerState;
	this->inputLayout = data.inputLayout;

}

void Renderable::SetTexture(int index,int texture)
{
	if(index == 0)
		baseTexture = texture;
	if(1 == index)
		normalMap = texture;
}


